/*
   Copyright (C) 2011 Roman Belov <romblv@gmail.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "lang/parser.h"
#include "lang/lexer.h"
#include "lang/signal.h"
#include "lib/base.h"
#include "util/list.h"
#include "util/link.h"

/* This function construct a independent copy of the module with all local
 * modules recursively.
 * */

static struct module *
module_duplicate(struct module *m)
{
	struct module	*d;
	struct module	*ml, *mld;
	struct link	*ll, *lld;
	struct port	*pl, *pld;
	struct spec	*sl, *sld;
	link_node_t	*lk;
	const char	*lid;
	int		len, i;

	d = malloc(sizeof(struct module));

	d->id = strdup(m->id);
	d->lid = NULL;

	list_up(&d->local);
	list_up(&d->port);
	list_up(&d->link);
	list_up(&d->spec);

	d->calc = m->calc;
	d->cust = NULL;

	if (m->lmap) {

		len = 0;

		while (m->lmap[len] != NULL) ++len;

		d->lmap = malloc((len + 1) * sizeof(struct port *));
		d->lmap[len] = NULL;
	}
	else
		d->lmap = NULL;

	list_for_each(&m->local, ml) {
		mld = module_duplicate(ml);
		list_insert(&d->local, mld);
		mld->lid = strdup(ml->lid);
	}

	list_for_each(&m->port, pl) {

		pld = malloc(sizeof(struct port));
		list_insert(&d->port, pld);

		pld->id = strdup(pl->id);
		pld->bits = pl->bits;
		pld->owner = d;
		pld->lni = NULL;
		pld->lne = NULL;
	}

	if (d->lmap) {

		i = 0;

		list_for_each(&d->port, pld) {
			d->lmap[i++] = pld;
		}
	}

	list_for_each(&m->link, ll) {

		lld = malloc(sizeof(struct link));
		list_insert(&d->link, lld);

		list_up(&lld->list);
		lld->val = ll->val;
		lld->pval = lld->val;
		lld->owner = d;
		lld->master = NULL;

		list_for_each(&ll->list, lk) {

			pl = lk->pl;

			if (pl->owner == m) {
				list_for_each(&d->port, pld) {

					if (strcmp(pld->id, pl->id) == 0) {
						list_insert(&lld->list, link_node_alloc(pld));
						pld->lni = lld;
						break;
					}
				}
			}
			else {
				lid = pl->owner->lid;

				list_for_each(&d->local, mld) {

					if (strcmp(mld->lid, lid) == 0) {
						/* OK */
						break;
					}
				}

				list_for_each(&mld->port, pld) {

					if (strcmp(pld->id, pl->id) == 0) {
						list_insert(&lld->list, link_node_alloc(pld));
						pld->lne = lld;
						break;
					}
				}
			}
		}
	}

	list_for_each(&m->spec, sl) {

		sld = malloc(sizeof(struct spec));
		list_insert(&d->spec, sld);

		sld->id = strdup(sl->id);
		sld->type = sl->type;

		if (sl->type == SPEC_STRING)
			sld->val.s = strdup(sl->val.s);
		else
			sld->val = sl->val;

		sld->owner = sl->owner;
	}

	return d;
}

static void
in_module_place(struct parser *ps, struct lexer *lex, struct module *m)
{
	struct module	*s, *d, *l;
	int		err = 0;
	int		inc = 1;

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_SYMBOL) {

		s = module_find(ps, lex->tok.val);

		if (s != NULL) {
			/* OK */
		}
		else {
			fprintf(stderr, "%s:%i: parser: module \"%s\" should be declared early\n",
					lex->fname, lex->line, lex->tok.val);
			ps->error++; err = 1;
		}
	}
	else if (lex->tok.id == TOKEN_SEMICOLON) {
		fprintf(stderr, "%s:%i: parser: useless expression with place keyword\n",
				lex->fname, lex->line);
		ps->error++; return;
	}
	else {
		fprintf(stderr, "%s:%i: parser: module identifier is expected\n",
				lex->fname, lex->line);
		ps->error++; err = 1;
	}

	do {
		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_SYMBOL) {

			if (err) continue;

			list_for_each(&m->local, l) {
				if (strcmp(l->lid, lex->tok.val) == 0) {
					fprintf(stderr, "%s:%i: parser: local module \"%s\" had already been"
							" declared\n",
							lex->fname, lex->line, lex->tok.val);
					ps->error++; err = 1;
				}
			}

			d = module_duplicate(s);
			list_insert(&m->local, d);

			d->lid = strdup(lex->tok.val);

			inc = 0;
		}
		else if (lex->tok.id == TOKEN_SEMICOLON)
			break;
		else {
			fprintf(stderr, "%s:%i: parser: local module identifier or semicolon is expected\n",
					lex->fname, lex->line);
			ps->error++; err = 1; break;
		}
	}
	while (1);

	if (inc) {
		fprintf(stderr, "%s:%i: parser: incomplete expression with place keyword\n",
				lex->fname, lex->line);
		ps->error++;
	}
}

static void
in_module_port(struct parser *ps, struct lexer *lex, struct module *m)
{
	struct port	*p;
	int		bits = 0;
	int		err = 0;
	int		inc = 1;

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_IN)
		bits = SIGNAL_PORT_IN;
	else if (lex->tok.id == TOKEN_OUT)
		bits = SIGNAL_PORT_OUT;
	else if (lex->tok.id == TOKEN_INOUT)
		bits = SIGNAL_PORT_INOUT;
	else if (lex->tok.id == TOKEN_SEMICOLON) {
		fprintf(stderr, "%s:%i: parser: useless expression with port keyword\n",
				lex->fname, lex->line);
		ps->error++; return;
	}
	else {
		fprintf(stderr, "%s:%i: parser: in or out or inout keyword is expected\n",
				lex->fname, lex->line);
		ps->error++; err = 1;
	}

	do {
		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_SYMBOL) {

			if (err) continue;

			list_for_each(&m->port, p) {

				if (strcmp(p->id, lex->tok.val) == 0) {
					fprintf(stderr, "%s:%i: parser: port \"%s\" had already been declared early\n",
							lex->fname, lex->line, lex->tok.val);
					ps->error++; err = 1;
				}
			}

			if (err == 0) {

				p = malloc(sizeof(struct port));
				list_insert(&m->port, p);

				p->id = strdup(lex->tok.val);
				p->owner = m;
				p->bits = bits;
				p->lni = NULL;
				p->lne = NULL;

				inc = 0;
			}
		}
		else if (lex->tok.id == TOKEN_SEMICOLON)
			break;
		else {
			fprintf(stderr, "%s:%i: parser: port identifier or semicolon is expected\n",
					lex->fname, lex->line);
			ps->error++; err = 1; break;
		}
	}
	while(1);

	if (inc) {
		fprintf(stderr, "%s:%i: parser: incomplete expression with port keyword\n",
				lex->fname, lex->line);
		ps->error++;
	}
}

static struct link *
module_port_get_side_link(struct port *lp, struct module *m)
{
	if (lp->owner == m)
		return lp->lni;
	else
		return lp->lne;
}

static void
module_port_set_side_link(struct port *lp, struct module *m, struct link *ln)
{
	if (lp->owner == m)
		lp->lni = ln;
	else
		lp->lne = ln;
}

static void
module_port_link(struct port *lp, struct link *ln, struct module *m)
{
	struct port	*p;
	struct link	*l;
	link_node_t	*lk, *lkn;

	l = module_port_get_side_link(lp, m);

	if (l) {

		if (l == ln) return;

		/* This port already linked so we need to merge these links.
		 * */

		list_for_each_safe(&l->list, lk, lkn) {

			p = lk->pl;
			module_port_set_side_link(p, m, ln);

			list_remove(lk);
			list_insert(&ln->list, lk);
		}

		list_remove(l);
		free(l);
	}

	list_insert(&ln->list, link_node_alloc(lp));
	module_port_set_side_link(lp, m, ln);
}

static void
in_module_link(struct parser *ps, struct lexer *lex, struct module *m)
{
	struct module	*l;
	struct link	*ln;
	struct port	*p;
	list_node_t	unils;
	link_node_t	*lk, *lkn;
	char		id[LEXER_SYM_MAX + 1];
	int		defval;
	int		fail;
	int		err = 0;

	list_up(&unils);
	defval = SIGNAL_INVALID;

	do {
		lexer_token_next(lex);

iml_do_token:

		if (lex->tok.id == TOKEN_SYMBOL) {

			strcpy(id, lex->tok.val);

			lexer_token_next(lex);

			if (lex->tok.id == TOKEN_DOT) {

				lexer_token_next(lex);

				if (lex->tok.id == TOKEN_SYMBOL) {

					fail = 1;

					list_for_each(&m->local, l) {
						if (strcmp(l->lid, id) == 0) {
							fail = 0;
							break;
						}
					}

					if (fail) {
						fprintf(stderr, "%s:%i: parser: undeclared local module \"%s\"\n",
								lex->fname, lex->line, id);
						ps->error++; err = 1; continue;
					}

					fail = 1;

					list_for_each(&l->port, p) {

						if (strcmp(p->id, lex->tok.val) == 0) {
							list_insert(&unils, link_node_alloc(p));
							fail = 0;
							break;
						}
					}

					if (fail) {
						fprintf(stderr, "%s:%i: parser: undeclared local module port \"%s\"\n",
								lex->fname, lex->line, lex->tok.val);
						ps->error++; err = 1; continue;
					}
				}
				else if (lex->tok.id == TOKEN_SEMICOLON) {
					fprintf(stderr, "%s:%i: parser: incomplete expression after dot keyword\n",
							lex->fname, lex->line);
					ps->error++; err = 1; break;
				}
				else {
					fprintf(stderr, "%s:%i: parser: port identifier of local module is expected"
							" after the dot\n",
							lex->fname, lex->line);
					ps->error++; err = 1; continue;
				}
			}
			else {
				fail = 1;

				list_for_each(&m->port, p) {

					if (strcmp(p->id, id) == 0) {
						list_insert(&unils, link_node_alloc(p));
						fail = 0;
						break;
					}
				}

				if (fail) {
					fprintf(stderr, "%s:%i: parser: undeclared local port \"%s\"\n",
							lex->fname, lex->line, id);
					ps->error++; err = 1; continue;
				}

				goto iml_do_token;
			}
		}
		else if (lex->tok.id == TOKEN_PLUS) {

			if (defval == SIGNAL_INVALID)
				defval = SIGNAL_PULL_UP;
			else {
				fprintf(stderr, "%s:%i: parser: plus sign overrides default value %d\n",
						lex->fname, lex->line, defval);
				ps->error++; err = 1;
			}
		}
		else if (lex->tok.id == TOKEN_MINUS) {

			if (defval == SIGNAL_INVALID)
				defval = SIGNAL_PULL_DOWN;
			else {
				fprintf(stderr, "%s:%i: parser: minus sign overrides default value %d\n",
						lex->fname, lex->line, defval);
				ps->error++; err = 1;
			}
		}
		else if (lex->tok.id == TOKEN_SEMICOLON)
			break;
		else {
			fprintf(stderr, "%s:%i: parser: identifier or semicolon or sign is expected\n",
					lex->fname, lex->line);
			ps->error++; err = 1; break;
		}
	}
	while (1);

	/* Do not continue with errors.
	 * */

	if (err) return;

	if (list_is_empty(&unils)) {
		fprintf(stderr, "%s:%i: parser: useless expression with link\n",
				lex->fname, lex->line);
		ps->error++; return;
	}

	/* Assume default value of the link if it was not explicitly specified.
	 * */

	if (defval == SIGNAL_INVALID)
		defval = SIGNAL_PULL_DOWN;

	/* Look for existing link structure.
	 * */

	ln = NULL;

	list_for_each(&unils, lk) {
		p = lk->pl;
		ln = module_port_get_side_link(p, m);
		if (ln) break;
	}

	/* If there are no linked ports yet, create new link.
	 * */

	if (ln == NULL) {

		ln = malloc(sizeof(struct link));

		list_up(&ln->list);
		ln->owner = m;
		ln->master = NULL;

		list_insert(&m->link, ln);
	}

	/* Force default value.
	 * */

	ln->val = defval;
	ln->pval = ln->val;

	/* Attach each port to the link.
	 * */

	list_for_each_safe(&unils, lk, lkn) {
		p = lk->pl;
		module_port_link(p, ln, m);
		link_node_free(lk);
	}
}

static void
in_module_lib(struct parser *ps, struct lexer *lex, struct module *m)
{
	struct port	*p;
	int		lmap_len, i;
	int		err = 0;

	if (m->calc) {
		fprintf(stderr, "%s:%i: parser: only one lib statement is allowed at module scope\n",
				lex->fname, lex->line);
		ps->error++; err = 1;
	}

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_SYMBOL) {
		/* OK */
	}
	else if (lex->tok.id == TOKEN_SEMICOLON) {
		fprintf(stderr, "%s:%i: parser: useless expression with lib keyword\n",
				lex->fname, lex->line);
		ps->error++; err = 1; return;
	}
	else {
		fprintf(stderr, "%s:%i: parser: library identifier is expected after lib keyword\n",
				lex->fname, lex->line);
		ps->error++; err = 1;
	}

	if (err == 0) {

		m->calc = lib_base_get_cacl(lex->tok.val);

		if (m->calc == NULL) {
			fprintf(stderr, "%s:%i: parser: library symbol \"%s\" does not exist\n",
					lex->fname, lex->line, lex->tok.val);
			ps->error++; err = 1;
		}

		lmap_len = 0;
		i = 0;

		list_for_each(&m->port, p) ++lmap_len;

		m->lmap = malloc((lmap_len + 1) * sizeof(struct port *));

		list_for_each(&m->port, p) {
			m->lmap[i++] = p;
		}

		m->lmap[i++] = NULL;
	}

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_SEMICOLON) {
		/* OK */
	}
	else {
		fprintf(stderr, "%s:%i: parser: semicolon is expected\n",
				lex->fname, lex->line);
		ps->error++; err = 1;
	}
}

static void
in_module_specify(struct parser *ps, struct lexer *lex, struct module *m)
{
	struct spec	*s;
	struct module	*l = m;
	int		fail;
	int		err = 0;

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_SYMBOL) {

		fail = 1;

		list_for_each(&m->local, l) {
			if (strcmp(l->lid, lex->tok.val) == 0) {
				fail = 0;
				break;
			}
		}

		if (fail) {
			fprintf(stderr, "%s:%i: parser: undeclared local module \"%s\" to specify\n",
					lex->fname, lex->line, lex->tok.val);
			ps->error++;
		}

		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_LEFT_BRACKET) {
			/* OK */
		}
		else if (lex->tok.id == TOKEN_SEMICOLON) {
			fprintf(stderr, "%s:%i: parser: incomplete expression with specify keyword\n",
					lex->fname, lex->line);
			ps->error++; err = 1; return;
		}
		else {
			fprintf(stderr, "%s:%i: parser: left bracket is expected\n",
					lex->fname, lex->line);
			ps->error++;
		}
	}
	else if (lex->tok.id == TOKEN_LEFT_BRACKET) {
		/* OK */
	}
	else if (lex->tok.id == TOKEN_SEMICOLON) {
		fprintf(stderr, "%s:%i: parser: useless expression with specify keyword\n",
				lex->fname, lex->line);
		ps->error++; err = 1; return;
	}
	else {
		fprintf(stderr, "%s:%i: parser: local module identifier or left bracket is expected\n",
				lex->fname, lex->line);
		ps->error++;
	}

	do {
		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_SYMBOL) {

			if (err) continue;

			s = spec_find(l, lex->tok.val);

			if (s == NULL) {

				s = malloc(sizeof(struct spec));

				s->id = strdup(lex->tok.val);
				s->type = SPEC_NULL;
				s->owner = l;

				list_insert(&l->spec, s);
			}
			else {
				if (s->type == SPEC_STRING)
					free((char *) s->val.s);
				s->type = SPEC_NULL;
			}

			lexer_token_next(lex);

			if (lex->tok.id == TOKEN_EQUALITY) {
				/* OK */
			}
			else {
				fprintf(stderr, "%s:%i: parser: equality token is expected\n",
						lex->fname, lex->line);
				ps->error++; err = 1; continue;
			}

			lexer_token_next(lex);

			if (lex->tok.id == TOKEN_STRING) {
				s->type = SPEC_STRING;
				s->val.s = strdup(lex->tok.val);
			}
			else if (lex->tok.id == TOKEN_NUMBER) {
				s->type = SPEC_INT;
				s->val.i = strtoul(lex->tok.val, NULL, 10);
			}
			else {
				fprintf(stderr, "%s:%i: parser: string or number token is expected\n",
						lex->fname, lex->line);
				ps->error++; err = 1; continue;
			}

			lexer_token_next(lex);

			if (lex->tok.id == TOKEN_SEMICOLON) {
				/* OK */
			}
			else {
				fprintf(stderr, "%s:%i: parser: semicolon is expected after equality statement\n",
						lex->fname, lex->line);
				ps->error++; err = 1; continue;
			}
		}
		else if (lex->tok.id == TOKEN_SEMICOLON)
			err = 0;
		else if (lex->tok.id == TOKEN_RIGHT_BRACKET)
			break;
		else {
			if (err < 2) {
				fprintf(stderr, "%s:%i: parser: unexpected token at specify scope\n",
						lex->fname, lex->line);
			}
			else if (err < 3) {
				fprintf(stderr, "%s:%i: parser: ... etc.\n",
						lex->fname, lex->line);
			}

			ps->error++; if (err++ > 7) break;
		}
	}
	while (1);
}

static void
in_module(struct parser *ps, struct lexer *lex)
{
	struct module	*m;
	int		noskip = 0;
	int		err = 0;

	lexer_token_next(lex);

	if (lex->tok.id == TOKEN_SYMBOL) {

		m = module_find(ps, lex->tok.val);

		if (m != NULL) {
			fprintf(stderr, "%s:%i: parser: module \"%s\" had already been declared early\n",
					lex->fname, lex->line, lex->tok.val);
			ps->error++;
		}
		else {
			m = malloc(sizeof(struct module));
			m->id = strdup(lex->tok.val);
			m->lid = 0;

			list_up(&m->local);
			list_up(&m->port);
			list_up(&m->link);
			list_up(&m->spec);

			m->calc = NULL;
			m->lmap = NULL;
			m->cust = NULL;

			noskip = 1;
		}

		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_LEFT_BRACKET) {
			/* OK */
		}
		else if (lex->tok.id == TOKEN_SEMICOLON) {
			fprintf(stderr, "%s:%i: parser: incomplete expression with module keyword\n",
					lex->fname, lex->line);
			ps->error++; return;
		}
		else {
			fprintf(stderr, "%s:%i: parser: left bracket is expected\n",
					lex->fname, lex->line);
			ps->error++;
		}

		do {
			lexer_token_next(lex);

			if (lex->tok.id == TOKEN_PLACE) {
				if (noskip) in_module_place(ps, lex, m);
			}
			else if (lex->tok.id == TOKEN_PORT) {
				if (noskip) in_module_port(ps, lex, m);
			}
			else if (lex->tok.id == TOKEN_LINK) {
				if (noskip) in_module_link(ps, lex, m);
			}
			else if (lex->tok.id == TOKEN_LIB) {
				if (noskip) in_module_lib(ps, lex, m);
			}
			else if (lex->tok.id == TOKEN_SPECIFY) {
				if (noskip) in_module_specify(ps, lex, m);
			}
			else if (lex->tok.id == TOKEN_SEMICOLON)
				;
			else if (lex->tok.id == TOKEN_RIGHT_BRACKET)
				break;
			else {
				if (err < 2) {
					fprintf(stderr, "%s:%i: parser: unexpected token at module scope\n",
							lex->fname, lex->line);
				}
				else if (err < 3) {
					fprintf(stderr, "%s:%i: parser: ... etc.\n",
							lex->fname, lex->line);
				}

				ps->error++; if (err++ > 7) break;
			}
		}
		while (1);
	}
	else if (lex->tok.id == TOKEN_SEMICOLON) {
		fprintf(stderr, "%s:%i: parser: useless expression with module keyword\n",
				lex->fname, lex->line);
		ps->error++; return;
	}
	else {
		fprintf(stderr, "%s:%i: parser: module identifier is expected after module keyword\n",
				lex->fname, lex->line);
		ps->error++;
	}

	if (noskip) {

		if (m->calc == NULL)
			m->calc = lib_base_get_default();

		list_insert(&ps->list, m);
	}
}

struct parser *
parser_open(const char *file,
		struct parser *p,
		const char *from_file,
		int line)
{
	struct parser	*ps = p;
	struct lexer	*lex;
	link_node_t	*lk;
	int		is_root = 0;
	int		err = 0;

	lex = lexer_source_open(file, from_file, line);

	if (lex == NULL) return NULL;

	if (ps == NULL) {

		/* Only root instance of this function may report the number
		 * of errors and destruct the parser.
		 * */

		is_root = 1;

		ps = malloc(sizeof(struct parser));

		list_up(&ps->list);
		list_up(&ps->file);
		ps->error = 0;
	}

	list_for_each(&ps->file, lk) {

		if (strcmp(lk->pl, lex->fname) == 0) {

			/* This file has already been parsed,
			 * just silently skip it.
			 * */

			lexer_source_close(lex);

			return ps;
		}
	}

	/* Store the file name to prevent it be parsed multiple times.
	 * */

	list_insert(&ps->file, link_node_alloc(strdup(lex->fname)));

	do {
		lexer_token_next(lex);

		if (lex->tok.id == TOKEN_USE) {

			err = 0;

			do {
				lexer_token_next(lex);

				if (lex->tok.id == TOKEN_STRING) {

					err = 0;

					if (parser_open(lex->tok.val, ps,
								lex->fname, lex->line) != ps) {
						fprintf(stderr, "%s:%i: parser: nothing to parse\n",
								lex->fname, lex->line);
						ps->error++;
					}
				}
				else if (lex->tok.id == TOKEN_SEMICOLON)
					break;
				else {
					if (err < 2) {
						fprintf(stderr, "%s:%i: parser: string or semicolon is"
								" expected after use keyword\n",
								lex->fname, lex->line);
					}
					else if (err < 3) {
						fprintf(stderr, "%s:%i: parser: ... etc.\n",
								lex->fname, lex->line);
					}

					ps->error++; if (err++ > 7) break;
				}
			}
			while (1);
		}
		else if (lex->tok.id == TOKEN_MODULE) {

			err = 0;

			in_module(ps, lex);
		}
		else if (lex->tok.id == TOKEN_SEMICOLON)
			;
		else if (lex->tok.id == TOKEN_NULL)
			break;
		else {
			if (err < 2) {
				fprintf(stderr, "%s:%i: parser: use or module statement is expected at global scope\n",
						lex->fname, lex->line);
			}
			else if (err < 3) {
				fprintf(stderr, "%s:%i: parser: ... etc.\n",
						lex->fname, lex->line);
			}

			ps->error++; err++;
		}
	}
	while (1);

	lexer_source_close(lex);

	if (ps->error && is_root) {

		/* It is a fail.
		 * */

		fprintf(stderr, "global: parser: %d syntax errors was detected\n",
				ps->error);

		parser_close(ps);
		ps = NULL;
	}

	return ps;
}

void
parser_close(struct parser *p)
{
	/* TODO: In normal case this will be called only once at the end
	 * so there are no memory leaks, that is why I did not do it.
	 * */
}

struct module *
module_find(struct parser *p, const char *id)
{
	struct module	*m;

	list_for_each(&p->list, m) {
		if (strcmp(m->id, id) == 0)
			return m;
	}

	return NULL;
}

struct spec *
spec_find(struct module *m, const char *id)
{
	struct spec	*s;

	list_for_each(&m->spec, s) {
		if (strcmp(s->id, id) == 0)
			return s;
	}

	return NULL;
}

